/******************************************************************************
 *  Filename:       sw_chacha.c
 ******************************************************************************/
/*
chacha-ref.c version 20080118
D. J. Bernstein
Public domain.
*/

#define ECRYPT_LITTLE_ENDIAN

#include "sw_ecrypt-sync.h"

#define ROTATE(v, c) (ROTL32(v, c))
#define XOR(v, w)    ((v) ^ (w))
#define PLUS(v, w)   (U32V((v) + (w)))
#define PLUSONE(v)   (PLUS((v), 1))

#define QUARTERROUND(a, b, c, d)        \
    x[a] = PLUS(x[a], x[b]);            \
    x[d] = ROTATE(XOR(x[d], x[a]), 16); \
    x[c] = PLUS(x[c], x[d]);            \
    x[b] = ROTATE(XOR(x[b], x[c]), 12); \
    x[a] = PLUS(x[a], x[b]);            \
    x[d] = ROTATE(XOR(x[d], x[a]), 8);  \
    x[c] = PLUS(x[c], x[d]);            \
    x[b] = ROTATE(XOR(x[b], x[c]), 7);

static void salsa20_wordtobyte(u8 output[64], const u32 input[16])
{
    u32 x[16];
    int i;

    for (i = 0; i < 16; ++i)
    {
        x[i] = input[i];
    }
    for (i = 8; i > 0; i -= 2)
    {
        QUARTERROUND(0, 4, 8, 12)
        QUARTERROUND(1, 5, 9, 13)
        QUARTERROUND(2, 6, 10, 14)
        QUARTERROUND(3, 7, 11, 15)
        QUARTERROUND(0, 5, 10, 15)
        QUARTERROUND(1, 6, 11, 12)
        QUARTERROUND(2, 7, 8, 13)
        QUARTERROUND(3, 4, 9, 14)
    }
    for (i = 0; i < 16; ++i)
    {
        x[i] = PLUS(x[i], input[i]);
    }
    for (i = 0; i < 16; ++i)
    {
        U32TO8_LITTLE(output + 4 * i, x[i]);
    }
}

void ECRYPT_init(void)
{
    return;
}

static const char sigma[16] = "expand 32-byte k";
static const char tau[16]   = "expand 16-byte k";

void ECRYPT_keysetup(ECRYPT_ctx *x, const u8 *k, u32 kbits, u32 ivbits)
{
    const char *constants;

    x->input[4] = U8TO32_LITTLE(k + 0);
    x->input[5] = U8TO32_LITTLE(k + 4);
    x->input[6] = U8TO32_LITTLE(k + 8);
    x->input[7] = U8TO32_LITTLE(k + 12);
    if (kbits == 256)
    { /* recommended */
        k += 16;
        constants = sigma;
    }
    else
    { /* kbits == 128 */
        constants = tau;
    }
    x->input[8]  = U8TO32_LITTLE(k + 0);
    x->input[9]  = U8TO32_LITTLE(k + 4);
    x->input[10] = U8TO32_LITTLE(k + 8);
    x->input[11] = U8TO32_LITTLE(k + 12);
    x->input[0]  = U8TO32_LITTLE(constants + 0);
    x->input[1]  = U8TO32_LITTLE(constants + 4);
    x->input[2]  = U8TO32_LITTLE(constants + 8);
    x->input[3]  = U8TO32_LITTLE(constants + 12);
}

void ECRYPT_ivsetup(ECRYPT_ctx *x, const u8 *iv)
{
    x->input[12] = 0;
    x->input[13] = 0;
    x->input[14] = U8TO32_LITTLE(iv + 0);
    x->input[15] = U8TO32_LITTLE(iv + 4);
}

void ECRYPT_encrypt_bytes(ECRYPT_ctx *x, const u8 *m, u8 *c, u32 bytes)
{
    u8 output[64];
    int i;

    if (!bytes)
    {
        return;
    }
    for (;;)
    {
        salsa20_wordtobyte(output, x->input);
        x->input[12] = PLUSONE(x->input[12]);
        if (!x->input[12])
        {
            x->input[13] = PLUSONE(x->input[13]);
            /* stopping at 2^70 bytes per nonce is user's responsibility */
        }
        if (bytes <= 64)
        {
            for (i = 0; i < bytes; ++i)
            {
                c[i] = m[i] ^ output[i];
            }
            return;
        }
        for (i = 0; i < 64; ++i)
        {
            c[i] = m[i] ^ output[i];
        }
        bytes -= 64;
        c += 64;
        m += 64;
    }
}

void ECRYPT_decrypt_bytes(ECRYPT_ctx *x, const u8 *c, u8 *m, u32 bytes)
{
    ECRYPT_encrypt_bytes(x, c, m, bytes);
}

void ECRYPT_keystream_bytes(ECRYPT_ctx *x, u8 *stream, u32 bytes)
{
    u32 i;
    for (i = 0; i < bytes; ++i)
    {
        stream[i] = 0;
    }
    ECRYPT_encrypt_bytes(x, stream, stream, bytes);
}
